package org.xlp.fraction;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * @version 1.0
 * @author 徐龙平
 *         <p>
 *         2017-5-7
 *         </p>
 * 
 *         分数抽象类，功能主要有分数的四则运算，分数化简
 */
public abstract class IFraction<T extends IFraction, V extends Number> extends Number implements Serializable,
		Comparable<IFraction>, Cloneable {
	private static final long serialVersionUID = -2471621653241765623L;

    /**
     * 全为零字符串正则
     */
    protected static final String ZERO_REGEX = "^[0]*$";

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T add(long fraction);

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T add(byte fraction){
		return add((long) fraction);
	}

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T add(short fraction){
		return add((long) fraction);
	}

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T add(int fraction){
		return add((long) fraction);
	}

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T add(double fraction);

	/**
	 * 分数加法
	 * 
	 * @param fraction
	 * @return 返回this
	 */
	public T add(float fraction){
		return add((double)fraction);
	}

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T add(T fraction);

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T subtract(long fraction);

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T subtract(byte fraction){
		return subtract((long)fraction);
	}

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T subtract(short fraction){
		return subtract((long)fraction);
	}

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T subtract(int fraction){
		return subtract((long)fraction);
	}

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T subtract(double fraction);

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T subtract(float fraction){
		return subtract((double) fraction);
	}

	/**
	 * 分数减法
	 * 
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T subtract(T fraction);

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T multiply(long fraction);

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T multiply(byte fraction){
		return multiply((long)fraction);
	}

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T multiply(short fraction){
		return multiply((long)fraction);
	}

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T multiply(int fraction){
		return multiply((long)fraction);
	}

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T multiply(double fraction);

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T multiply(float fraction){
		return multiply((double) fraction);
	}

	/**
	 * 分数乘法
	 * 
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T multiply(T fraction);

	/**
	 * 分数除法
	 * 
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T divide(T fraction);

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T divide(long fraction);

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T divide(byte fraction){
		return divide((long)fraction);
	}

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T divide(short fraction){
		return divide((long)fraction);
	}

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T divide(int fraction){
		return divide((long)fraction);
	}

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public abstract T divide(double fraction);

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	public T divide(float fraction){
		return divide((double) fraction);
	}

	/**
	 * 取反
	 * 
	 * @return 返回一个与this相反的分数新对象
	 */
	public abstract T negate();

	/**
	 * 取绝对值
	 * 
	 * @return
	 */
	public abstract T abs();

	/**
	 * 计算this的n次幂（n =0,1,2,3,....）
	 * 
	 * @param n
	 *            n次幂
	 * @return this的n次幂
	 */
	public abstract T pow(int n);

	/**
	 * 得到最简分数
	 * 
	 * @return
	 */
	public abstract T getFraction();

	/**
	 * 分数转换成指定的精度的小数BigDecimal
	 * 
	 * @param accuracy
	 *            假如accuracy小于0，则重新设置为0
	 * @return
	 */
	public abstract BigDecimal decimalValue(int accuracy);

	/**
	 * 分数转换成小数BigDecimal
	 * 
	 * @return
	 */
	public abstract BigDecimal decimalValue();

	/**
	 * 分数转换成BigInteger
	 * @return
	 */
	public abstract BigInteger bigIntegerValue();

	/**
	 * 分数转换成指定的精度的小数
	 * 
	 * @param accuracy
	 *            假如accuracy小于0，则重新设置为0
	 * @return
	 */
	public double doubleValue(int accuracy) {
		return decimalValue(accuracy).doubleValue();
	}
	
	/**
	 * 得到两个分数中较大的那个分数
	 * 
	 * @param fraction
	 *            要比较的分数
	 * @return 假如this > fraction反回this，否则返回fraction
	 */
	@SuppressWarnings("unchecked")
	public T max(T fraction) {
		return compareTo(fraction) > 0 ? (T) this : fraction;
	}

	/**
	 * 得到两个分数中较小的那个分数
	 * 
	 * @param fraction
	 *            要比较的分数
	 * @return 假如this < fraction反回this，否则返回fraction
	 */
	@SuppressWarnings("unchecked")
	public T min(T fraction) {
		return compareTo(fraction) < 0 ? (T) this : fraction;
	}

	/**
	 * 获取分数的分子
	 * 
	 * @return
	 */
	public abstract V getNumerator();

	/**
	 * 获取分数的分母
	 * 
	 * @return
	 */
	public abstract V getDenominator();

	@Override
	public int intValue() {
		return (int) longValue();
	}

	@Override
	public float floatValue() {
		return (float) doubleValue();
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof IFraction)) return false;
		return compareTo((IFraction) obj) == 0;
	}

    /**
     * 比较分数对象的大小
     * @param fraction
     * @return -1： 被比较分数大，0：相等，1：本分数大
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    @Override
	public int compareTo(IFraction fraction){
        Number n1 = getNumerator();
        Number d1 = getDenominator();
        Number n2 = fraction.getNumerator();
        Number d2 = fraction.getDenominator();
        BigInteger nb1, db1, nb2, db2, rn, rd;
        if (n1 instanceof BigInteger){
            nb1 = (BigInteger) n1;
            db1 = (BigInteger) d1;
        } else {
            nb1 = new BigInteger(n1.toString());
            db1 = new BigInteger(d1.toString());
        }
        if (n2 instanceof  BigInteger){
            nb2 = (BigInteger) n2;
            db2 = (BigInteger) d2;
        } else {
            nb2 = new BigInteger(n2.toString());
            db2 = new BigInteger(d2.toString());
        }
        if (db1.equals(db2)){
            rd = db2;
            rn = nb1.subtract(nb2);
        } else {
            rd = db1.multiply(db2);
            rn = nb1.multiply(db2);
            BigInteger t = nb2.multiply(db1);
            rn = rn.subtract(t);
        }
        if (rn.equals(BigInteger.ZERO)) return 0;

        return (rd.compareTo(BigInteger.ZERO) < 0 && rn.compareTo(BigInteger.ZERO) < 0)
            || (rd.compareTo(BigInteger.ZERO) > 0 && rn.compareTo(BigInteger.ZERO) > 0)
                ? 1 : -1;
    }
}
